package com.mezz.bones.framework.outbox.processor;

import com.mezz.bones.framework.outbox.domain.entity.OutboxMessage;
import com.mezz.bones.framework.outbox.domain.entity.OutboxMessageDlq;
import com.mezz.bones.framework.outbox.enums.MessageStatusEnum;
import com.mezz.bones.framework.outbox.executor.MessageExecutor;
import com.mezz.bones.framework.outbox.factor.ExecutorFactory;
import com.mezz.bones.framework.outbox.model.ExecuteResult;
import com.mezz.bones.framework.outbox.repository.IOutboxMessageDlqRepository;
import com.mezz.bones.framework.outbox.repository.IOutboxMessageRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.Objects;

@Slf4j
@Component
@RequiredArgsConstructor
public class OutboxMessageProcessor {

    private final ExecutorFactory executorFactory;
    private final IOutboxMessageRepository outboxMessageService;
    private final IOutboxMessageDlqRepository outboxMessageDlqRepository;


    /**
     * 处理单个消息
     *
     * @param message 待处理的消息
     * @return 处理结果
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean processMessage(OutboxMessage message) {

        log.info("开始处理消息: {}", message.getMessageId());

        try {

            // 幂等性检查
            if (Objects.equals(message.getStatus(), MessageStatusEnum.COMPLETED.getCode())) {
                log.info("消息已处理完成，跳过执行: {}", message.getMessageId());
                return true;
            }

            // 检查重试次数是否达到最大值
            int maxRetry = message.getMaxRetry() != null ? message.getMaxRetry() : 3;
            if (message.getRetryCount() >= maxRetry) {
                log.warn("消息重试次数已达到最大值，标记为最终失败: {}", message.getMessageId());
                //移动至死信队列表
                moveToDeadLetter(message);
                return false;
            }

            // 获取执行器
            MessageExecutor executor = executorFactory.getExecutor(message.getExecuteType());

            // 执行消息
            ExecuteResult result = executor.execute(message);

            // 更新消息状态
            updateMessageStatus(message, result);

            return result.isSuccess();
        } catch (Exception e) {
            log.error("处理消息异常: {}", message.getMessageId(), e);

            // 更新为失败状态
            message.setStatus(3); // 失败状态
            message.setLastExecuteResult("执行异常: " + e.getMessage());
            message.setErrorStack(e.toString());
            message.setLastExecuteTime(new Date());
            message.setRetryCount(message.getRetryCount() + 1);

            // 计算下次重试时间
            calculateNextRetryTime(message);

            outboxMessageService.updateById(message);
            return false;
        }
    }

    /**
     * 更新消息状态
     */
    private void updateMessageStatus(OutboxMessage message, ExecuteResult result) {
        // 设置执行时间
        Date now = new Date();
        if (message.getFirstExecuteTime() == null) {
            message.setFirstExecuteTime(now);
        }
        message.setLastExecuteTime(now);

        // 更新执行结果
        message.setLastExecuteResult(result.getData().toString());
        message.setUpdateTime(now);

        if (result.isSuccess()) {
            // 执行成功
            message.setStatus(MessageStatusEnum.COMPLETED.getCode()); // 已完成状态
            message.setCompleteTime(now);
        } else {
            // 执行失败
            message.setStatus(MessageStatusEnum.FAILED.getCode()); // 失败状态
            message.setRetryCount(message.getRetryCount() + 1);
            message.setErrorStack(result.getErrorStack());

            // 计算下次重试时间
            calculateNextRetryTime(message);
        }

        // 更新消息
        outboxMessageService.updateById(message);
    }

    /**
     * 计算下次重试时间
     */
    private void calculateNextRetryTime(OutboxMessage message) {
        // 简单的指数退避策略
        int retryCount = message.getRetryCount();
        int maxRetry = message.getMaxRetry() != null ? message.getMaxRetry() : 3;

        if (retryCount >= maxRetry) {
            // 超过最大重试次数，不再重试
            message.setNextRetryTime(null);
            return;
        }

        // 指数退避: 5s, 25s, 125s...
        long delaySeconds = (long) Math.pow(5, retryCount);
        // 最大延迟不超过1小时
        delaySeconds = Math.min(delaySeconds, 3600);

        Date nextRetryTime = new Date(System.currentTimeMillis() + delaySeconds * 1000);
        message.setNextRetryTime(nextRetryTime);
    }

    /**
     * 将消息转移到死信队列
     */
    private void moveToDeadLetter(OutboxMessage message) {
        // 创建死信队列记录
        OutboxMessageDlq dlqMsg = new OutboxMessageDlq();

        dlqMsg.setMessageId(message.getMessageId());
        dlqMsg.setBusinessType(message.getBusinessType());
        dlqMsg.setBusinessKey(message.getBusinessKey());
        dlqMsg.setExecuteType(message.getExecuteType());
        dlqMsg.setExecuteTarget(message.getExecuteTarget());
        dlqMsg.setExecuteMethod(message.getExecuteMethod());
        dlqMsg.setExecuteParams(message.getExecuteParams());
        dlqMsg.setStatus(message.getStatus());
        dlqMsg.setRetryCount(message.getRetryCount());
        dlqMsg.setMaxRetry(message.getMaxRetry());
        dlqMsg.setNextRetryTime(message.getNextRetryTime());
        dlqMsg.setExecuteRetryPolicy(message.getExecuteRetryPolicy());
        dlqMsg.setCreateTime(message.getCreateTime());
        dlqMsg.setUpdateTime(message.getUpdateTime());
        dlqMsg.setEventTime(message.getEventTime());
        dlqMsg.setFirstExecuteTime(message.getFirstExecuteTime());
        dlqMsg.setLastExecuteTime(message.getLastExecuteTime());
        dlqMsg.setCompleteTime(message.getCompleteTime());
        dlqMsg.setLastExecuteResult(message.getLastExecuteResult());
        dlqMsg.setErrorStack(message.getErrorStack());
        dlqMsg.setExtParams(message.getExtParams());
        dlqMsg.setTags(message.getTags());
        dlqMsg.setPriority(message.getPriority());
        dlqMsg.setVersion(message.getVersion());
        dlqMsg.setDeadReason("重试次数已达到最大值");
        dlqMsg.setIsDel(0);

        // 保存死信队列记录
        outboxMessageDlqRepository.save(dlqMsg);

        // 更新原消息状态为已取消
        message.setStatus(MessageStatusEnum.CANCELLED.getCode());
        message.setUpdateTime(new Date());
        outboxMessageService.updateById(message);
    }
}